const LFUNode = function (
    key = "",
    val = "",
    freq = 0,
    pre = null,
    next = null
  ) {
    this.key = key;
    this.val = val;
    this.freq = freq;
    this.pre = pre;
    this.next = next;
  };
  class LFULinkLine{
    constructor(node){
        this.init(node);
    }
    init(node){
        let head = new LFUNode("head");
        let tail = new LFUNode("tail");
        head.next = node;
        tail.pre = node;
        node.pre = head;
        node.next = tail;
        this.head = head;
        this.tail = tail;
    }
    addNode(node){
        this.head.next.pre = node;
        node.pre = this.head;
        node.next = this.head.next;
        this.head.next = node;
    }
    removeNode(node){
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }
  }
  
  /**
   * @param {number} capacity
   */
  class LFUCache{
    constructor(capacity = 100){
        this.init(capacity);
    }
    init(capacity){
        this.capacity = capacity;
        this.num = 0;
        this.minFreq = 0;
        this.kvMap = new Map();
        this.freqMap = new Map();
    }
    /**
     * @param {number} key
     * @return {number}
     */
    get(key){
        if (this.capacity === 0) return -1;
        if (!this.kvMap.has(key)) return -1;
        let node = this.kvMap.get(key);
        let linkLine = this.freqMap.get(node.freq);
        linkLine.removeNode(node);
        //清空了
        if (linkLine.head.next === linkLine.tail) {
          this.freqMap.delete(node.freq);
          if (this.minFreq == node.freq) this.minFreq++;
        }
        node.freq++;
        if (this.freqMap.has(node.freq)) {
          linkLine = this.freqMap.get(node.freq);
          linkLine.addNode(node);
        } else {
          this.freqMap.set(node.freq, new LFULinkLine(node));
        }
        return node.val;
    }
    /**
     * @param {number} key
     * @param {number} value
     * @return {void}
     */
    put (key, value) {
      if (this.capacity === 0) return;
      if (this.kvMap.has(key)) {
        //更新
        let node = this.kvMap.get(key);
        node.val = value;
        let linkLine = this.freqMap.get(node.freq);
        linkLine.removeNode(node);
        if (linkLine.head.next === linkLine.tail) {
          if (this.minFreq == node.freq) this.minFreq++;
          this.freqMap.delete(node.freq);
        }
        node.freq++;
        if (this.freqMap.has(node.freq)) {
          linkLine = this.freqMap.get(node.freq);
          linkLine.addNode(node);
        } else {
          linkLine = new LFULinkLine(node);
          this.freqMap.set(node.freq, linkLine);
        }
      } else {
        //存入
        if (this.capacity == this.num) {
          //存满
          let freq = this.minFreq;
          let linkLine = this.freqMap.get(freq);
          let node = linkLine.tail.pre;
          linkLine.removeNode(node);
          this.kvMap.delete(node.key);
          if (linkLine.head.next === linkLine.tail) {
            this.freqMap.delete(node.freq);
          }
        } else {
          this.num++;
        }
        let node = new LFUNode(key, value, 0);
        this.kvMap.set(key, node);
        if (this.freqMap.has(0)) {
          let linkLine = this.freqMap.get(0);
          linkLine.addNode(node);
        } else {
          let linkLine = new LFULinkLine(node);
          this.freqMap.set(0, linkLine);
        }
        this.minFreq = 0;
      }
    };
  };
  
  /**
   * Your LFUCache object will be instantiated and called as such:
   * var obj = new LFUCache(capacity)
   * var param_1 = obj.get(key)
   * obj.put(key,value)
   */
  
exports.LFUCache = LFUCache;